# -*- coding: utf-8 -*-
import nltk
import re
import sys
import cPickle
from xml.dom.minidom import *

# Clase Nodo, para manejar eficientemente el análisis semántico
class Nodo:
	
	def __init__(self, tok, lex, ind):
		
		self.token = tok
		self.lexema = lex
		self.indice = ind
	
	def __str__( self ):
		return "Nodo(" + self.token + ", " + self.lexema + ", " + str(self.indice) + ")"
		
	def __repr__(self):
		return "Nodo(" + self.token + ", " + self.lexema + ", " + str(self.indice) + ")"
		
	def getToken(self):
		return self.token
	
	def setToken(self, tok):
		self.token = tok
	
	def getLexema(self):
		return self.lexema
	
	def setLexema(self, lex):
		self.lexema = lex
	
	def getIndice(self):
		return self.indice
	
	def setIndice(self, ind):
		self.indice = ind

#Procesador de terminales predefinidos
class terminalProcessor:
    dom = None
    def __init__(self, filename):
        global dom
        fsock = open(filename)
        xmlText = fsock.read()
        fsock.close()
        xmlText = xmlText.replace("\n","")
        xmlText = xmlText.replace("\r","")
        xmlText = xmlText.replace("\t","")
        dom = parseString(xmlText)

    def getTerminals(self):
        terminals = {}
        for noTerminal in dom.getElementsByTagName('noTerminal'):
            name = str(noTerminal.attributes["name"].value).encode('latin-1')
            definition = ""
            i = 1
            for terminal in noTerminal.childNodes:
                for palabra in terminal.childNodes:
                    definition += palabra.firstChild.data
                if (i<len(noTerminal.childNodes)):
                    definition += '|'
                i = i + 1
            definition = definition.encode('latin-1')
            terminals[name] = definition
        return terminals

# Gramática de compilador, no de intérprete
def genGramaticaKeywords(term_rep):
	
	# Construir los terminales
	terminales = terminalProcessor(term_rep)
	terms = terminales.getTerminals()

	# Calcular los keywords
	keyw = ""
	keyw = terms['vPlantear'] + terms['iCaller'] + terms['vScan'] + terms['vPresSubj'] + terms['arts'] 
	keyw = keyw + terms['sus'] + terms['preps'] + terms['vLigar'] + terms['vDligar'] + terms['vMov'] + terms['dirs'] + terms['longs']
	keyw = keyw + terms['hacer'] + terms['reps'] + terms['si'] + terms['sino']
	keyw = re.sub("'", " ", keyw)
	keyw = re.sub("\|", " ", keyw)
	keyw = keyw + " id num "
	keyw = keyw.split()
	
	# Generar la gramatica (Español 0.0.1)
	gString = """
		P -> PLA 'id' RES
		PLA -> """ + terms['vPlantear'] + """
		RES -> PAS RES | PAS
		PAS -> SCAN | LIG | DLIG | MOV | HACE | COND_P
		SCAN -> V_SCAN """ + terms['iCaller'] + """ V_PRES_SUBJ | V_SCAN ART SUS | V_SCAN PREP ART SUS
		V_SCAN -> """ + terms['vScan'] + """
		V_PRES_SUBJ -> """ + terms['vPresSubj'] + """
		ART -> """ + terms['arts'] + """
		SUS -> """ + terms['sus'] + """
		PREP -> """ + terms['preps'] + """
		LIG -> V_LIG ART SUS
		V_LIG -> """ + terms['vLigar'] + """
		DLIG -> V_DLIG ART SUS
		V_DLIG -> """ + terms['vDligar'] + """
		MOV -> V_MOV OBJETIVO
		V_MOV -> """ + terms['vMov'] + """
		OBJETIVO -> ART SUS | ART DIR LONG
		DIR -> """ + terms['dirs'] + """ 
		LONG -> """ + terms['longs'] + """
		HACE -> VERBO ART SUS | VERBO ART SUS REP
		VERBO -> """ + terms['hacer'] + """
		REP -> """ + terms['reps'] + """
		COND_P -> SI COND RES SI_NO RES
		SI -> """ + terms['si'] + """
		SI_NO -> """ + terms['sino'] + """
		COND -> 'xO'
		"""
	return gString, keyw

# Lexer
def lexer(lexemas):
	
	# Arreglo de identificadores
	idents = range(len(lexemas))
	
	# Ciclo de busqueda de identificadores
	for i in range(len(idents)):
	
		# Verificar si es algun keyword
		if(matchKw(lexemas[i])):
			idents[i] = lexemas[i]
		else:
			idents[i] = matchRegEx(lexemas[i])
			
	return idents

# Parser
def parser(tokens):
	
	# Parsear
	try:
	
		arbolParse = rd_parser.nbest_parse(tokens)
		
		# No se genero el arbol del parse
		if len(arbolParse) == 0:
			return None
		else:
			return arbolParse
		
	except ValueError, e:
		
		# Imprimir error
		print "Token desconocido:"
		print e
		return None
	
# Metodo para verificar coherencia y ejecutar la instruccion solicitada
def ejecucionSemantica(subArbol):
	
	# Variables de arbol
	sa_id = subArbol.node # Identificador de nodo
	sa_len = len(subArbol) # Cantidad de hijos
	
	# Variables encontradas en cada instruccion
	id_problema = ""
	verbo_scan = ""
	articulo = ""
	sustantivo = ""
	accion_externa = ""
	accion_interna = ""
	prposicion = ""
	objetivo = [0,0]
	direccion = [0,0]
	longitud = 0
	veces = 0
	
	# P -> PLA 'id' RES
	if sa_id == "P":
		id_problema = subArbol[1].getLexema()
			
		# Algoritmo para cargar el problema
		print "se desea resolver el problema " + id_problema
		
		# Paso recursivo sobre la solucion del problema
		ejecucionSemantica(subArbol[2], lexemas)
		
	# Colocar este paso recursivo es irrelevante, puesto que a la fecha (25/1/2011)
	# Da igual el verbo usado para identificar el problema a resolver		
	# PLA -> """ + terms['vPlantear'] + """
	#elif sa_id == "PLA":
	#	print ""
	
	# RES -> PAS RES | PAS
	elif sa_id == "RES":
		
		# RES -> PAS
		if sa_len == 1:
			ejecucionSemantica(subArbol[0], lexemas)
			
		# RES -> PAS RES
		elif sa_len == 2:
			ejecucionSemantica(subArbol[0], lexemas)
			ejecucionSemantica(subArbol[1], lexemas)
			
	# PAS -> SCAN | LIG | DLIG | MOV | HACE | COND_P
	elif sa_id == "PAS":
		
		# Estos ifs son innecesarios debido a que sea cual sea el tipo de PASO
		# El analizador semántico solo los utiliza de puente para llegar a las
		# Producciones importantes (26/1/2011)
		
		#pas_type = subArbol[0].node
		
		# PAS -> SCAN
		#if pas_type == "SCAN":
		#	print ""
			
		# PAS -> LIG 
		#elif pas_type == "LIG":
		#	print ""
			
		# PAS -> DLIG 
		#elif pas_type == "DLIG":
		#	print ""
			
		# PAS -> MOV 
		#elif pas_type == "MOV":
		#	print ""
			
		# PAS -> HACE 
		#elif pas_type == "HACE":
		#	print ""
			
		# PAS -> COND_P
		#elif pas_type == "COND_P":
		#	print ""
		
		ejecucionSemantica(subArbol[0], lexemas)
		
		# SCAN
	elif sa_id == "SCAN":
		
		verbo_scan = ejecucionSemantica(subArbol[0], lexemas)
		
		# SCAN -> V_SCAN """ + terms['iCaller'] + """ V_PRES_SUBJ
		if isinstance(subArbol[1], Nodo):
			subj_buscar = ejecucionSemantica(subArbol[sa_len - 1], lexemas)
			
			# Algoritmo para mostrar lo encontrado
			print "se desea buscar un objeto que pueda " + subj_buscar
		
		# SCAN -> V_SCAN ART SUS
		elif subArbol[1].node == "ART":
			articulo = ejecucionSemantica(subArbol[1], lexemas)
			sustantivo = ejecucionSemantica(subArbol[2], lexemas)
			
			# Algoritmo para mostrar lo encontrado
			print "se desea buscar " + articulo + " " + sustantivo
		
		# SCAN -> V_SCAN PREP ART SUS
		elif subArbol[1].node == "PREP":
			preposicion = ejecucionSemantica(subArbol[1], lexemas)
			articulo = ejecucionSemantica(subArbol[2], lexemas)
			sustantivo = ejecucionSemantica(subArbol[3], lexemas)
			
			# Algoritmo para mostrar lo encontrado
			print "se desea buscar " + preposicion + " " + articulo + " " + sustantivo
		
	# EVALUAR POSIBILIDAD DE LOS SIGUIENTES CASOS ELIMINARLOS PUESTO QUE
	# TODOS HACEN LO MISMO (26/1/2001), NO LO HICE PORQUE SE ME OCURRE QUE
	# EN UN FUTURO CERCANO CADA CASO PUEDE HACER COSAS DISTINTAS, COMO VERIFICACIONES <-------------------------------------------- REVISAR ESTO DESPUÉS
	# EN EL DICCIONARIO DE SINÓNIMOS
	
	# V_SCAN -> """ + terms['vScan'] + """
	elif sa_id == "V_SCAN":
		return subArbol[0].getLexema()
			
	# V_PRES_SUBJ -> """ + terms['vPresSubj'] + """
	elif sa_id == "V_PRES_SUBJ":
		return subArbol[0].getLexema()
		
	# ART -> """ + terms['arts'] + """
	elif sa_id == "ART":
		return subArbol[0].getLexema()
		
	# SUS -> """ + terms['sus'] + """	
	elif sa_id == "SUS":
		return subArbol[0].getLexema()
		
	# PREP -> """ + terms['preps'] + """ 	
	elif sa_id == "PREP":
		return subArbol[0].getLexema()
		
	# LIG -> V_LIG ART SUS	
	elif sa_id == "LIG":
		accion_interna = ejecucionSemantica(subArbol[0], lexemas)
		articulo = ejecucionSemantica(subArbol[1], lexemas)
		sustantivo = ejecucionSemantica(subArbol[2], lexemas)
		
		# Algoritmo para ejecutar la ligadura
		print "se desea ligar al heroe " + articulo + " " + sustantivo
		
	# V_LIG -> """ + terms['vLigar'] + """	
	elif sa_id == "V_LIG":
		return subArbol[0].getLexema()
		
	# DLIG -> V_DLIG ART SUS	
	elif sa_id == "DLIG":
		accion_interna = ejecucionSemantica(subArbol[0], lexemas)
		articulo = ejecucionSemantica(subArbol[1], lexemas)
		sustantivo = ejecucionSemantica(subArbol[2], lexemas)
		
		# Algoritmo para ejecutar la "desligadura"
		print "se desea desligar del heroe " + articulo + " " + sustantivo
		
	# V_DLIG -> """ + terms['vDligar'] + """	
	elif sa_id == "V_DLIG":
		return subArbol[0].getLexema()
		
	# MOV -> V_MOV OBJETIVO	
	elif sa_id == "MOV":
		accion_interna = ejecucionSemantica(subArbol[0], lexemas)
		objetivo = ejecucionSemantica(subArbol[1], lexemas)
		
		# Algoritmo para moverse
		print "se desea moverse a las coordenadas (" + str(objetivo[0]) + ", " + str(objetivo[1]) + ")"
		
	# V_MOV -> """ + terms['vMov'] + """	
	elif sa_id == "V_MOV":
		return subArbol[0].getLexema()
		
	# OBJERIVO
	elif sa_id == "OBJETIVO":
		
		# OBJETIVO -> ART SUS
		if sa_len == 2:
			articulo = ejecucionSemantica(subArbol[0], lexemas)
			sustantivo = ejecucionSemantica(subArbol[1], lexemas)
			
			# Algoritmo para determinar la posicion del 
			# objeto al cual dirigirnos
			return [3, 2] # <---------------------------------------------------------------------------------------------- MUESTRAS PARA EL DEBUG
			
		# OBJETIVO -> ART DIR LONG	
		elif sa_len == 3:
			articulo = ejecucionSemantica(subArbol[0], lexemas)
			direccion = ejecucionSemantica(subArbol[1], lexemas)
			longitud = ejecucionSemantica(subArbol[2], lexemas)
			
			# Algoritmo para determinar la posicion del 
			# objeto al cual dirigirnos
			return [5, 4] # <---------------------------------------------------------------------------------------------- MUESTRAS PARA EL DEBUG
			
	# DIR -> """ + terms['dirs'] + """ 
	elif sa_id == "DIR":
		
		# Algoritmo para determinar la direccion con 
		# la que debemos movernos
		return [1, 0] # <--------------------------------------------------------------------------------------------------- MUESTRAS PARA EL DEBUG
		
	# LONG -> """ + terms['longs'] + """
	elif sa_id == "LONG":
		
		# Algoritmo para determinar que tanto nos 
		# vamos a mover
		return 0
		
	# HACE 
	elif sa_id == "HACE":
		
		accion_externa = ejecucionSemantica(subArbol[0], lexemas)
		articulo = ejecucionSemantica(subArbol[1], lexemas)
		sustantivo = ejecucionSemantica(subArbol[2], lexemas)
		
		# HACE -> VERBO ART SUS
		if sa_len == 3:	
			
			# Algoritmo para ejecutar la acción
			print "se desea que el heroe haga: " + accion_externa + " " + articulo + " " + sustantivo
			
		# HACE -> VERBO ART SUS REP	
		elif sa_len == 4:
			veces = ejecucionSemantica(subArbol[3], lexemas)
			
			# Algoritmo para ejecutar la acción un número de veces
			print "se desea que el heroe haga: " + accion_externa + " " + articulo + " " + sustantivo
			print str(veces) + " veces"
			
	# VERBO -> """ + terms['hacer'] + """
	elif sa_id == "VERBO":
		return subArbol[0].getLexema()
		
	# REP -> """ + terms['reps'] + """
	elif sa_id == "REP":
		
		# Algoritmo para calcular la cantidad de veces que una acción
		# externa se repetirá
		return 5 # <-------------------------------------------------------------------------------------------------------- MUESTRASS PARA EL DEBUG
		
	# COND_P -> SI COND RES SI_NO RES
	elif sa_id == "COND_P":
		print ""
		
	# SI -> """ + terms['si'] + """
	elif sa_id == "SI":
		print ""
		
	# SI_NO -> """ + terms['sino'] + """
	elif sa_id == "SI_NO":
		print ""
		
	# 	
	elif sa_id == "COND":
		print ""
		
# Regresa el nombre del regex con el cual hay match
def matchRegEx(str):
	
	matchObj = re.match( r'[a-z]([a-z 0-9 _]*)', str)
	
	if matchObj:
		return "id"
	
	matchObj = re.match( r'[0-9]([0-9]*)', str)
	
	if matchObj:
		return "num"
	
	return "err"

# Verifica si str es un keyword
def matchKw(str):
	for i in range(len(keywords)):
		if str == keywords[i]:
			return True
	return False

# Hace un recorrido enOrden sobre el arbol para cambiar terminales (str) por Nodos
def nodificar(subArbol, lexemas, siguiente_indice):
	
	# No debo dejar que en un paso recursivo mande un str de parámetro
	sa_len = len(subArbol)
	
	# Para cada nodo hijo (subarbol)
	for i in range(sa_len):
		
		# Si el subarbol actual es del tipo str
		if isinstance(subArbol[i], str):
			
			# Caso base:
			# Convertir el str en un Nodo
			nu_nodo = Nodo(subArbol[i], lexemas[siguiente_indice], siguiente_indice)
			subArbol[i] = nu_nodo
			siguiente_indice = siguiente_indice + 1
			
		else:
		
			# Paso recursivo
			siguiente_indice = nodificar(subArbol[i], lexemas, siguiente_indice)
			
	return siguiente_indice

# ------------- 
#     TEST 
# -------------

# Construccion de la gramatica, tabla de keywords y el parser
gramString, keywords = genGramaticaKeywords("terminales.xml")
gramatica= nltk.parse_cfg(gramString)
rd_parser = rd_parser = nltk.RecursiveDescentParser(gramatica)

# Obtener string de ingreso
nombreArchivo = ""

try:
	nombreArchivo = sys.argv[1]
except IndexError:
	print "\n (!) Error: es necesario que proveas un nombre de un archivo a ejecutar\n"
	sys.exit(0)
	
try:
	
	f = open(nombreArchivo, "r")
	inString = f.read()
	inString = inString.lower()
	
except IOError:
	print "\n (!) Error: archivo no encontrado.\n"
	sys.exit(0)

# Split (calcular lexemas)
lexemas = inString.split()

# Calcular identificadores de lexemas
tokens = lexer(lexemas)

# Calcular el arbol de parse de la entrada
arbolDeParse = parser(tokens)

# Cambiar strings de los terminales en objetos Nodo
nodificar(arbolDeParse[0], lexemas, 0)

if(arbolDeParse != None):
	
	arbolDeParse[0].draw()
	#ejecucionSemantica(arbolDePare[0])

else:
	print "\n (!) Parser fallo al intentar generar el arbol: verifique la entrada.\n"